﻿// ....................................................................................................................
//	Author: Copyright (C) 2012 Ing. Marek Hubal
//	Email: hubal@acasoftware.org
// ....................................................................................................................

using System;

namespace core
{
    /// <summary>Trieda INodeList_GZIP</summary>
    public class INodeList_GZIP : INodeList
    {
        protected string FBaseArchiveFileName; // Nazov archivu
        protected string FRelativePath; // Relativna cesta

        protected bool FExpand_Expanded; // Priznak vykonania extrahovania node zo suboru
        protected System.IO.BinaryReader FExpand_ArchiveFile; // Stream pre archiv

        protected System.IO.BinaryReader FExtract_ArchiveFile; // Stream pre pracu v extract mode
        protected System.IO.Compression.GZipStream FExtract_ArchiveDataFile; // Datovy stream pre pracu v extract mode
        protected INode FExtract_CurrentItem; // Node pre aktualne spracovany zaznam

        /// <summary>Otvorenie cache pre nacitanie</summary>
        /// <param name="pCacheID">ID cache</param>
        /// <returns>true / false</returns>
        protected override bool BeginReadCache(string pCacheID)
        {
            return false;
        }
        /// <summary>Otvorenie cache pre zapis</summary>
        /// <param name="pCacheID">ID cache</param>
        /// <returns>true / false</returns>
        protected override bool BeginWriteCache(string pCacheID)
        {
            return false;
        }

        /// <summary>Konstruktor</summary>
        /// <param name="pArchiveFileName">nazov suboru archivu</param>
        /// <param name="pRelativePath">relativna cesta v archive</param>
        public INodeList_GZIP(string pArchiveFileName = "", string pRelativePath = "")
        {
            ID = INodeList.NODE_LIST_ID_GZIP_ARCHIVE;
            FColsCount = 7;
            FEnableVisibilityMask = 0x7e; FDefVisibilityMask = 0x7f;
            FThreadExpandAvailable = true;
            FBaseArchiveFileName = pArchiveFileName; FRelativePath = pRelativePath;
            FExpand_ArchiveFile = null;
            FExtract_ArchiveFile = null;
            FExtract_CurrentItem = null;
        }

        /// <summary>Ziskanie nazvu stlpca</summary>
        /// <param name="pIndex">pozicia</param>
        /// <returns>nazov alebo prazdny retazec</returns>
        public override string GetColumnName(int pIndex)
        {
            switch (pIndex)
            {
                case 0: return lang.NODE_LIST_GZIP_ARCHIVE_TEXT_1;
                case 1: return lang.NODE_LIST_GZIP_ARCHIVE_TEXT_2;
                case 2: return lang.NODE_LIST_GZIP_ARCHIVE_TEXT_3;
                case 3: return lang.NODE_LIST_GZIP_ARCHIVE_TEXT_4;
                case 4: return lang.NODE_LIST_GZIP_ARCHIVE_TEXT_5;
                case 5: return lang.NODE_LIST_GZIP_ARCHIVE_TEXT_6;
                case 6: return lang.NODE_LIST_GZIP_ARCHIVE_TEXT_7;
                default: return "";
            }
        }
        /// <summary>Ziskanie zarovnania stlpca</summary>
        /// <param name="pIndex">pozicia</param>
        /// <returns>zarovnanie</returns>
        public override System.Windows.Forms.HorizontalAlignment GetColumnAlignment(int pIndex)
        {
            switch (pIndex)
            {
                case 2:
                case 3:
                    return System.Windows.Forms.HorizontalAlignment.Right;
                case 4:
                case 5:
                case 6:
                    return System.Windows.Forms.HorizontalAlignment.Center;
                default: return System.Windows.Forms.HorizontalAlignment.Left;
            }
        }
        /// <summary>Property: CurrentPath</summary>
        public override string CurrentPath
        {
            get
            {
                return IFileSystem.CreateFullArchivePath(FBaseArchiveFileName, FRelativePath);
            }
        }
        /// <summary>Property: KeyValue</summary>
        public override string KeyValue
        {
            get
            {
                return IFileSystem.ExtractName(FBaseArchiveFileName).ToLower();
            }
        }
        /// <summary>Property: Test ci je list archivom</summary>
        public override bool IsArchive
        {
            get
            {
                return true;
            }
        }
        /// <summary>Property: IsCopySource</summary>
        public override bool IsCopySource
        {
            get
            {
                return true;
            }
        }
        /// <summary>Property: IsCopySourceItem</summary>
        public override bool IsCopySourceItem(int pIndex)
        {
            if (!IsCopySource) return false; // Test celku
            INode N = this[pIndex];
            if (N == null) return false; // Neplatny node?
            switch (N.ID)
            {
                case INode.NODE_ID_BACK_ITEM: return false;
                default: return true;
            }
        }
        /// <summary>Property: ArchiveName</summary>
        public override string ArchiveName
        {
            get
            {
                return FBaseArchiveFileName;
            }
        }
        /// <summary>Property: DeviceRootPath</summary>
        public override string DeviceRootPath
        {
            get
            {
                return FBaseArchiveFileName;
            }
        }
        /// <summary>Property: BasePath</summary>
        public override string BasePath
        {
            get
            {
                return FBaseArchiveFileName;
            }
        }

        /// <summary>Zaciatok expandovania obsahu</summary>
        /// <param name="pFilter">objekt filtra</param>
        /// <returns>0 alebo kod chyby</returns>
        public override int BeginExpand(INodeFilters pFilter = null)
        {
            FState = 1; // Nastavim stav expandovania
            try
            {
                FExpand_ArchiveFile = new System.IO.BinaryReader(System.IO.File.OpenRead(FBaseArchiveFileName)); // Otvorim stream archivu
                if (FExpand_ArchiveFile.ReadByte() != 0x1f) return -1; // ID1
                if (FExpand_ArchiveFile.ReadByte() != 0x8b) return -1; // ID2
                if (FExpand_ArchiveFile.ReadByte() != 8) return -1; // CM
            }
            catch
            {
                return -1; // Nie je mozne otvorit archiv
            }

            FExpand_Expanded = false;
            return 0;
        }
        /// <summary>Uzatvorenie aktualneho expandu</summary>
        public override void CloseExpand()
        {
            FExpand_ArchiveFile = (System.IO.BinaryReader)ICore.DisposeObject(FExpand_ArchiveFile); // Zrusim stream
            FState = 0; // Nastavim normalny stav
        }
        /// <summary>Expandovanie dalsieho zaznamu</summary>
        /// <param name="Result">ulozeny obsah najdeneho zaznamu alebo null ak sa ma zaznam pripojit do zoznamu</param>
        /// <returns>0 - ziaden dalsi zaznam, 1 - najdeny zaznam alebo kod chyby</returns>
        public override int ExpandNext(INodeFilters pFilter = null, INode Result = null)
        {
            try
            {
                if (FExpand_Expanded) return 0; // Uz som expandoval?
                if (Result == null) { INode N1 = new INode(); N1.ID = INode.NODE_ID_BACK_ITEM; AddNode(N1); } // Vytvorim prvy back item node

                try
                {
                    byte FLG = FExpand_ArchiveFile.ReadByte(); // FLG
                    uint MTIME = FExpand_ArchiveFile.ReadUInt32(); // MTIME
                    byte XFL = FExpand_ArchiveFile.ReadByte(); // XFL
                    byte OS = FExpand_ArchiveFile.ReadByte(); // OS

                    if ((FLG & 0x4) != 0) // FEXTRA pritomne?
                    {
                        ushort L = FExpand_ArchiveFile.ReadUInt16(); // Dlzka
                        FExpand_ArchiveFile.BaseStream.Position += (int)L; // Preskocim
                    }

                    string FileName = null;
                    if ((FLG & 0x8) != 0) // FNAME pritomne?
                    {
                        System.Text.StringBuilder B = new System.Text.StringBuilder(); // Nacitam null terminated retazec
                        while (true)
                        {
                            byte B1 = FExpand_ArchiveFile.ReadByte();
                            if (B1 == 0) break;
                            B.Append((char)B1); // Zaradim znak
                        }
                        FileName = B.ToString();
                    }
                    else
                    {
                        FileName = IFileSystem.ExtractName(FBaseArchiveFileName); // Inak bude nazov nazov archivu
                    }

                    if ((FLG & 0x10) != 0) // FCOMMENT pritomne?
                    {
                        while (FExpand_ArchiveFile.ReadByte() != 0) ;
                    }

                    if ((FLG & 0x2) != 0) // FHCRC pritomne?
                    {
                        FExpand_ArchiveFile.ReadUInt16();
                    }

                    FExpand_ArchiveFile.BaseStream.Position = FExpand_ArchiveFile.BaseStream.Length - 8;
                    uint CRC32 = FExpand_ArchiveFile.ReadUInt32();
                    uint ISIZE = FExpand_ArchiveFile.ReadUInt32();

                    INode N = Result == null ? new INode() : Result;
                    N.ID = INode.NODE_ID_GZIP_ITEM; N.NodeData = new INodeData_GZIP_ITEM();
                    ((INodeData_GZIP_ITEM)N.NodeData).GZip_ItemName = FileName; ((INodeData_GZIP_ITEM)N.NodeData).GZip_OriginalSize = ISIZE; ((INodeData_GZIP_ITEM)N.NodeData).GZip_CompressedSize = (long)FExpand_ArchiveFile.BaseStream.Position;
                    ((INodeData_GZIP_ITEM)N.NodeData).GZip_CRC = CRC32; ((INodeData_GZIP_ITEM)N.NodeData).GZip_OS = OS; ((INodeData_GZIP_ITEM)N.NodeData).GZip_ModifyDate = MTIME;
                    if (Result == null) AddNode(N);
                }
                catch
                {
                    return -1; // Chyba citania
                }

                FExpand_Expanded = true; // Posledny node
                return 1;
            }
            catch
            {
                return -1;
            }
        }

        /// <summary>Vytvorenie textov pre zobrazenie</summary>
        /// <param name="pNode">objekt node</param>
        public override void CreateDisplayStrings(INode pNode)
        {
            if (pNode.DisplayStrings != null) return; // Uz su vytvorenie?

            pNode.DisplayStrings = new string[ColsCount]; // Alokujem retazec
            switch (pNode.ID) // Podla typu node
            {
                case INode.NODE_ID_BACK_ITEM:
                    {
                        pNode.DisplayStrings[0] = ICore.BoundString("[", "..", "]", IApp.Settings.FileExplorer_DirsInBraces);
                        pNode.DisplayStrings[2] = lang.NODE_LIST_GZIP_ARCHIVE_TEXT_8;
                        pNode.DefaultImageIndex = -1;
                    } break;
                case INode.NODE_ID_GZIP_ITEM:
                    {
                        pNode.DisplayStrings[0] = pNode.NameOnly; // Len nazov
                        if (pNode.DisplayStrings[0] == "") pNode.DisplayStrings[0] = pNode.Name; else pNode.DisplayStrings[1] = pNode.Ext; // Pripona
                        pNode.DisplayStrings[2] = ICore.Size2String((ulong)((INodeData_GZIP_ITEM)pNode.NodeData).GZip_OriginalSize, IApp.Settings.FileExplorer_DisplaySizeMode); // Velkost
                        pNode.DisplayStrings[3] = ICore.Size2String((ulong)((INodeData_GZIP_ITEM)pNode.NodeData).GZip_CompressedSize, IApp.Settings.FileExplorer_DisplaySizeMode); // Komprimovana velkost
                        pNode.DisplayStrings[4] = ICore.DateTime2Str(pNode.DLastModify);
                        pNode.DisplayStrings[5] = INodeList_GZIP.OS2String(((INodeData_GZIP_ITEM)pNode.NodeData).GZip_OS);
                        pNode.DisplayStrings[6] = ((INodeData_GZIP_ITEM)pNode.NodeData).GZip_CRC.ToString("X8");
                        pNode.DefaultImageIndex = 1;
                    } break;
            }
        }
        /// <summary>Vytvorenie klonu zoznamu</summary>
        /// <returns>objekt zoznamu</returns>
        public override INodeList CloneList()
        {
            return new INodeList_GZIP(FBaseArchiveFileName, FRelativePath);
        }

        /// <summary>Spustenie procesu kontroly archivu GZIP</summary>
        /// <param name="pErrorMsg">chybova sprava</param>
        /// <param name="pProgressForm">progress formular</param>
        /// <returns>true / false</returns>
        public override bool TestArchive(out string pErrorMsg, IFileExplorer_ProgressForm_TestArchive pProgressForm = null)
        {
            pErrorMsg = ""; // Inicializacia
            if (pProgressForm != null) pProgressForm.StartArchive(FBaseArchiveFileName, "GZIP");

            byte FLG;
            uint MTIME;
            byte XFL;
            byte OS;
            string FileName;
            uint CRC32;
            uint ISIZE;

            try // Test hlavicky a nacitanie dat
            {
                using (System.IO.BinaryReader R = new System.IO.BinaryReader(System.IO.File.OpenRead(FBaseArchiveFileName))) // Otvorim reader
                {
                    if ((R.ReadByte() != 0x1f) || (R.ReadByte() != 0x8b) || (R.ReadByte() != 8))
                    {
                        pErrorMsg = lang.NODE_LIST_GZIP_ARCHIVE_TEXT_10_1;
                        return false; // Chyba
                    }

                    FLG = R.ReadByte(); // FLG
                    MTIME = R.ReadUInt32(); // MTIME
                    XFL = R.ReadByte(); // XFL
                    OS = R.ReadByte(); // OS

                    if ((FLG & 0x4) != 0) // FEXTRA pritomne?
                    {
                        ushort L = R.ReadUInt16(); // Dlzka
                        R.BaseStream.Position += (int)L; // Preskocim
                    }

                    FileName = null;
                    if ((FLG & 0x8) != 0) // FNAME pritomne?
                    {
                        System.Text.StringBuilder B = new System.Text.StringBuilder(); // Nacitam null terminated retazec
                        while (true)
                        {
                            byte B1 = R.ReadByte();
                            if (B1 == 0) break;
                            B.Append((char)B1); // Zaradim znak
                        }
                        FileName = B.ToString();
                    }
                    else
                    {
                        FileName = IFileSystem.ExtractName(FBaseArchiveFileName); // Inak bude nazov nazov archivu
                    }

                    if ((FLG & 0x10) != 0) // FCOMMENT pritomne?
                    {
                        while (R.ReadByte() != 0) ;
                    }

                    if ((FLG & 0x2) != 0) // FHCRC pritomne?
                    {
                        R.ReadUInt16();
                    }

                    R.BaseStream.Position = R.BaseStream.Length - 8;
                    CRC32 = R.ReadUInt32();
                    ISIZE = R.ReadUInt32();
                }
            }
            catch
            {
                pErrorMsg = lang.NODE_LIST_GZIP_ARCHIVE_TEXT_10;
                return false; // Chyba
            }

            if (pProgressForm != null) pProgressForm.StartFile(FileName); // Zaradim subor

            try // Test datovej casti
            {
                byte[] Buffer = new byte[65536];

                // Otvorim stream
                using (System.IO.Compression.GZipStream GS = new System.IO.Compression.GZipStream(System.IO.File.OpenRead(FBaseArchiveFileName), System.IO.Compression.CompressionMode.Decompress))
                {
                    while (true)
                    {
                        int C = GS.Read(Buffer, 0, 65536); // Citam data
                        if (C == 0) break;
                        if ((pProgressForm != null) && (pProgressForm.WaitForTerminate.BoolValue)) return false;
                    }
                }

                if (pProgressForm != null) pProgressForm.FinishFile(); // Ukoncim subor
                return true; // OK
            }
            catch
            {
                pErrorMsg = lang.NODE_LIST_GZIP_ARCHIVE_TEXT_10;
                return false; // Chyba
            }
        }
        /// <summary>Ziskanie statistickych informacii pre proces extrahovania</summary>
        /// <param name="ResultStatData">vysledna stat. data</param>
        /// <param name="pItems">zoznam poloziek pre spocitanie</param>
        /// <param name="pBreakState">premenna pre oznamenie o preruseni spracovania</param>
        /// <returns>true / false</returns>
        public override bool Run_GetExtractStatsInfo(IComputeOccupiedSpaceData ResultStatData, System.Collections.Generic.List<string> pItems = null, IThrSafeVariable pBreakState = null)
        {
            byte FLG;
            uint MTIME;
            byte XFL;
            byte OS;
            string FileName;
            uint CRC32;
            uint ISIZE;

            try // Test hlavicky a nacitanie dat
            {
                using (System.IO.BinaryReader R = new System.IO.BinaryReader(System.IO.File.OpenRead(FBaseArchiveFileName))) // Otvorim reader
                {
                    if ((R.ReadByte() != 0x1f) || (R.ReadByte() != 0x8b) || (R.ReadByte() != 8))
                    {
                        return false; // Chyba
                    }

                    FLG = R.ReadByte(); // FLG
                    MTIME = R.ReadUInt32(); // MTIME
                    XFL = R.ReadByte(); // XFL
                    OS = R.ReadByte(); // OS

                    if ((FLG & 0x4) != 0) // FEXTRA pritomne?
                    {
                        ushort L = R.ReadUInt16(); // Dlzka
                        R.BaseStream.Position += (int)L; // Preskocim
                    }

                    FileName = null;
                    if ((FLG & 0x8) != 0) // FNAME pritomne?
                    {
                        System.Text.StringBuilder B = new System.Text.StringBuilder(); // Nacitam null terminated retazec
                        while (true)
                        {
                            byte B1 = R.ReadByte();
                            if (B1 == 0) break;
                            B.Append((char)B1); // Zaradim znak
                        }
                        FileName = B.ToString();
                    }
                    else
                    {
                        FileName = IFileSystem.ExtractName(FBaseArchiveFileName); // Inak bude nazov nazov archivu
                    }

                    if ((FLG & 0x10) != 0) // FCOMMENT pritomne?
                    {
                        while (R.ReadByte() != 0) ;
                    }

                    if ((FLG & 0x2) != 0) // FHCRC pritomne?
                    {
                        R.ReadUInt16();
                    }

                    R.BaseStream.Position = R.BaseStream.Length - 8;
                    CRC32 = R.ReadUInt32();
                    ISIZE = R.ReadUInt32();

                    ResultStatData.FilesCount++;
                    ResultStatData.TotalSize += ISIZE;
                }
            }
            catch
            {
                return false; // Chyba
            }
            return true; // Vsetko OK
        }
        /// <summary>Spustenie hladania nad zoznamom</summary>
        /// <param name="pData">udaje pre hladanie</param>
        /// <param name="pProgressForm">progress formular</param>
        /// <returns>true / false</returns>
        public override bool Run_Search(ISearchDataStruct pData, int pDepth, IFileExplorer_ProgressForm_Search pProgressForm = null)
        {
            try // Test hlavicky a nacitanie dat
            {
                if (BeginExpand() != 0) return true; // Chyba?
                using (INode N = new INode())
                {
                    if (ExpandNext(null, N) != 1) return true; // Chyba?
                    CloseExpand();

                    string FullPath = FBaseArchiveFileName + ":\\" + N.Name;

                    bool IsIn = false; // Priznak platneho testu nazvu
                    if (pData.SearchPartialName) // Cast mena
                    {
                        if ((pData.SearchNameValue == "") || (INodeFilter.WildcardMatch(N.Name.ToLower(), pData.SearchNameValue.ToLower()))) // Wildcars porovnanie
                        {
                            if (pData.SearchExtFilter.TestNode(N)) // Test filtra
                            {
                                if (!pData.SearchInContent) // Nehladat v obsahu
                                {
                                    if (pProgressForm != null) pProgressForm.AddResult(FullPath, N.Size, false, N.Attributes, N.DLastModify, true, N.ID); // Zaradim ako vysledok hladania
                                }
                                IsIn = true; // Oznacim ze presiel
                            }
                        }
                    }
                    else
                    {
                        if ((pData.SearchNameValue == "") || (string.Compare(N.Name, pData.SearchNameValue, true) == 0)) // Test plneho mena
                        {
                            if (pData.SearchExtFilter.TestNode(N)) // Test filtra
                            {
                                if (!pData.SearchInContent) // Nehladat v obsahu?
                                {
                                    if (pProgressForm != null) pProgressForm.AddResult(FullPath, N.Size, false, N.Attributes, N.DLastModify, true, N.ID); // Zaradim ako vysledok hladania
                                }
                                IsIn = true; // Oznacim ze presiel
                            }
                        }
                    }

                    if ((IsIn) && (pData.SearchInContent)) // Prehladavat obsah?
                    {
                        if (string.IsNullOrEmpty(pData.SearchContentValue)) // Hladana hodnota je prazdna? - obsah sa prehlasi za true
                        {
                            if (!pData.SearchInContent_NotInContent) // Zaradovat true?
                            {
                                if (pProgressForm != null) pProgressForm.AddResult(FullPath, N.Size, false, N.Attributes, N.DLastModify, true, N.ID); // Zaradim ako vysledok hladania
                            }
                        }
                        else
                        {
                            // Idem prehladavat obsah
                            using (ISearchInContentStream CntStream = new ISearchInContentStream()) // Vytvorim hladaci stream
                            {
                                try
                                {
                                    using (System.IO.Compression.GZipStream GZ = new System.IO.Compression.GZipStream(System.IO.File.OpenRead(FBaseArchiveFileName), System.IO.Compression.CompressionMode.Decompress))
                                    {
                                        CntStream.Init(pData.SearchContentValue, pData.SearchInContent_CaseSensitive, pData.SearchInContent_EncodingType); // Inicializujem stream
                                        CntStream.GetData += (pBuffer, pStart, pMaxLength) => // Event na dodanie dat streamu
                                        {
                                            try
                                            {
                                                return GZ.Read(pBuffer, pStart, pMaxLength); // Citam
                                            }
                                            catch
                                            {
                                                return 0;
                                            }
                                        };
                                        int Res = CntStream.Find(pProgressForm); // Hladam
                                        if (Res == ISearchInContentStream.FIND_YES) // Nasiel som?
                                        {
                                            if (!pData.SearchInContent_NotInContent) // Zaradoivat true vysledky?
                                            {
                                                if (pProgressForm != null) pProgressForm.AddResult(FullPath, N.Size, false, N.Attributes, N.DLastModify, true, N.ID); // Zaradim ako vysledok hladania
                                            }
                                        }
                                        else // Nenasiel som
                                        {
                                            if (pData.SearchInContent_NotInContent) // Zaradovat false vysledky?
                                            {
                                                if (pProgressForm != null) pProgressForm.AddResult(FullPath, N.Size, false, N.Attributes, N.DLastModify, true, N.ID); // Zaradim ako vysledok hladania
                                            }
                                        }
                                    }
                                }
                                catch // Pri chybe jednoducho nic nezaradim
                                {
                                }
                            }
                        }
                    }

                }
            }
            catch
            {
                return true; // Chyba
            }
            return true;
        }

        /// <summary>Nastavenie zoznamu do modu zdroja extrahovania</summary>
        /// <param name="pItemsToExtract">polozky na extrahovanie</param>
        /// <param name="pProgressForm">progress formular</param>
        public override void BeginSourceExtractMode(System.Collections.Generic.List<string> pItemsToExtract = null, IFileExplorer_ProgressForm_ExtractArchive pProgressForm = null)
        {
            FExtract_ArchiveFile = null;
            FExtract_CurrentItem = new INode();
            CopyMode_NativeDataBuffer = new byte[MAX_COPY_BUFFER_SIZE];
            FExtractMode_ItemsToExpandList = null;
        }
        /// <summary>Uzatvorenie modu extrahovania</summary>
        public override void CloseExtractMode()
        {
            FExtract_ArchiveFile = (System.IO.BinaryReader)ICore.DisposeObject(FExpand_ArchiveFile);
            FExtract_ArchiveDataFile = (System.IO.Compression.GZipStream)ICore.DisposeObject(FExtract_ArchiveDataFile);
            FExtract_CurrentItem = (INode)ICore.DisposeObject(FExtract_CurrentItem);
            CopyMode_NativeDataBuffer = null;
        }
        /// <summary>Ziskanie dalsieho zaznamu na extrahovanie</summary>
        /// <param name="Result">vysledne data</param>
        /// <returns>0 - koniec, 1 - platny zaznam, alebo kod chyby</returns>
        public override int ExtractMode_GetNextSourceItem(INode Result)
        {
            if (FExtract_ArchiveFile != null) return 0; // Uz som nacital obsah?
            try
            {
                FExtract_ArchiveFile = new System.IO.BinaryReader(System.IO.File.OpenRead(FBaseArchiveFileName));

                byte FLG;
                uint MTIME;
                byte XFL;
                byte OS;
                string FileName;
                uint CRC32;
                uint ISIZE;

                if ((FExtract_ArchiveFile.ReadByte() != 0x1f) || (FExtract_ArchiveFile.ReadByte() != 0x8b) || (FExtract_ArchiveFile.ReadByte() != 8))
                {
                    return -1; // Chyba
                }

                FLG = FExtract_ArchiveFile.ReadByte(); // FLG
                MTIME = FExtract_ArchiveFile.ReadUInt32(); // MTIME
                XFL = FExtract_ArchiveFile.ReadByte(); // XFL
                OS = FExtract_ArchiveFile.ReadByte(); // OS

                if ((FLG & 0x4) != 0) // FEXTRA pritomne?
                {
                    ushort L = FExtract_ArchiveFile.ReadUInt16(); // Dlzka
                    FExtract_ArchiveFile.BaseStream.Position += (int)L; // Preskocim
                }

                FileName = null;
                if ((FLG & 0x8) != 0) // FNAME pritomne?
                {
                    System.Text.StringBuilder B = new System.Text.StringBuilder(); // Nacitam null terminated retazec
                    while (true)
                    {
                        byte B1 = FExtract_ArchiveFile.ReadByte();
                        if (B1 == 0) break;
                        B.Append((char)B1); // Zaradim znak
                    }
                    FileName = B.ToString();
                }
                else
                {
                    FileName = IFileSystem.ExtractName(FBaseArchiveFileName); // Inak bude nazov nazov archivu
                }

                if ((FLG & 0x10) != 0) // FCOMMENT pritomne?
                {
                    while (FExtract_ArchiveFile.ReadByte() != 0) ;
                }

                if ((FLG & 0x2) != 0) // FHCRC pritomne?
                {
                    FExtract_ArchiveFile.ReadUInt16();
                }

                FExtract_ArchiveFile.BaseStream.Position = FExtract_ArchiveFile.BaseStream.Length - 8;
                CRC32 = FExtract_ArchiveFile.ReadUInt32();
                ISIZE = FExtract_ArchiveFile.ReadUInt32();

                FExtract_CurrentItem.ID = INode.NODE_ID_GZIP_ITEM; // Ulozim do aktualnej polozky
                FExtract_CurrentItem.NodeData = new INodeData_GZIP_ITEM();
                ((INodeData_GZIP_ITEM)FExtract_CurrentItem.NodeData).GZip_ItemName = FileName; ((INodeData_GZIP_ITEM)FExtract_CurrentItem.NodeData).GZip_OriginalSize = ISIZE; ((INodeData_GZIP_ITEM)FExtract_CurrentItem.NodeData).GZip_CompressedSize = (long)FExtract_ArchiveFile.BaseStream.Position;
                ((INodeData_GZIP_ITEM)FExtract_CurrentItem.NodeData).GZip_CRC = CRC32; ((INodeData_GZIP_ITEM)FExtract_CurrentItem.NodeData).GZip_OS = OS; ((INodeData_GZIP_ITEM)FExtract_CurrentItem.NodeData).GZip_ModifyDate = MTIME;
                FExtract_CurrentItem.RelativeName = FileName;

                Result.ID = FExtract_CurrentItem.ID; // Skopirujem do vysledku
                Result.NodeData = new INodeData_GZIP_ITEM();
                ((INodeData_GZIP_ITEM)Result.NodeData).GZip_ItemName = ((INodeData_GZIP_ITEM)FExtract_CurrentItem.NodeData).GZip_ItemName;
                ((INodeData_GZIP_ITEM)Result.NodeData).GZip_OriginalSize = ((INodeData_GZIP_ITEM)FExtract_CurrentItem.NodeData).GZip_OriginalSize;
                ((INodeData_GZIP_ITEM)Result.NodeData).GZip_CompressedSize = ((INodeData_GZIP_ITEM)FExtract_CurrentItem.NodeData).GZip_CompressedSize;
                ((INodeData_GZIP_ITEM)Result.NodeData).GZip_CRC = ((INodeData_GZIP_ITEM)FExtract_CurrentItem.NodeData).GZip_CRC;
                ((INodeData_GZIP_ITEM)Result.NodeData).GZip_OS = ((INodeData_GZIP_ITEM)FExtract_CurrentItem.NodeData).GZip_OS;
                ((INodeData_GZIP_ITEM)Result.NodeData).GZip_ModifyDate = ((INodeData_GZIP_ITEM)FExtract_CurrentItem.NodeData).GZip_ModifyDate;
                Result.RelativeName = FExtract_CurrentItem.RelativeName;

                return 1;
            }
            catch
            {
                return -1; // Chyba?
            }
        }
        /// <summary>Otvorenie aktualneho suboru na citanie</summary>
        /// <returns>0 alebo kod chyby</returns>
        public override int ExtractMode_OpenCurrentFileForRead()
        {
            try
            {
                FExtract_ArchiveDataFile = new System.IO.Compression.GZipStream(System.IO.File.OpenRead(FBaseArchiveFileName), System.IO.Compression.CompressionMode.Decompress);
                return 0;
            }
            catch
            {
                return IFileSystem.FS_ERROR_UNKNOWN;
            }
        }
        /// <summary>Uzatvorenie aktualneho suboru otvoreneho na citanie</summary>
        public override void ExtractMode_CloseReadFile()
        {
            FExtract_ArchiveDataFile = (System.IO.Compression.GZipStream)ICore.DisposeObject(FExtract_ArchiveDataFile);
        }
        /// <summary>Nacitanie obsahu</summary>
        /// <param name="pMaxLength">max. dlzka</param>
        /// <param name="pResultBuffer">vysledny buffer alebo null pre interny</param>
        /// <returns>pocet nacitanych bajtov alebo kod chyby</returns>
        public override int ExtractMode_ReadFile(int pMaxLength, byte[] pResultBuffer)
        {
            try
            {
                return FExtract_ArchiveDataFile.Read(pResultBuffer, 0, pMaxLength);
            }
            catch
            {
                return IFileSystem.FS_ERROR_UNKNOWN;
            }
        }
        /// <summary>Ziskanie nazvu aktualneho spracovavaneho zaznamu</summary>
        /// <returns>nazov</returns>
        public override string ExtractMode_CurrentName()
        {
            return FExtract_CurrentItem.Name;
        }

        /// <summary>Spustenie procesu vytvorenia archivu</summary>
        /// <param name="pData">data</param>
        /// <param name="pItems">zoznam poloziek na archivaciu</param>
        /// <param name="pProgressForm">progress formular</param>
        /// <returns>true / false</returns>
        public static bool Run_CreateArchive_GZIP(ICompressionDataStr pData, System.Collections.Generic.List<string> pItems, IFileExplorer_ProgressForm_CompressItems pProgressForm = null)
        {
            IConfirmationFlags Flags = new IConfirmationFlags();
            byte[] Buffer = new byte[INodeList.MAX_COPY_BUFFER_SIZE]; // Buffer

            INodeList SourceList = INodeList.CreateNodeList(IFileSystem.ExtractPath(pItems[0]));
            if (SourceList == null) return false; // Chybna zdrojova cesta?
            using (SourceList)
            {
                INodeList DestList = INodeList.CreateNodeList(IFileSystem.ExtractPath(pData.DestinationArchiveName));
                if (DestList == null) return false; // Chybna cielova cesta?
                using (DestList)
                {
                    SourceList.BeginSourceCopyMode();
                    DestList.BeginDestinationCopyMode();

                    bool IsFolder; bool Skip;
                    foreach (string FN in pItems)
                    {
                        if ((pProgressForm != null) && (pProgressForm.WaitForTerminate.BoolValue)) return false;

                        string RelFN = SourceList.CopyMode_MakeRelative(FN, out IsFolder);
                        if (RelFN == "") continue;
                        if (IsFolder) continue; // Preskocim priecinok

                        string DestFN = pItems.Count == 1 ? IFileSystem.ExtractName(pData.DestinationArchiveName) : RelFN + ".gz"; // Vytvorim vysledne meno archivu

                        Skip = false;
                        int R;
                        while (true)
                        {
                            R = SourceList.CopyMode_OpenReadFile(RelFN, null); // Otvorim zdrojovy subor
                            if (R == 0) break;
                            switch (R)
                            {
                                default:
                                    {
                                        if (!pProgressForm.ShowEditor_ChooseFromList(string.Format(lang.FILE_EXPLORER_TEXT_39_7_1, SourceList.CopyMode_MakeSourceFullPath(RelFN)), lang.FILE_EXPLORER_TEXT_39_7_2, lang.FILE_EXPLORER_TEXT_39_7_3, ref Flags.OpenSourceFileError)) return false;
                                    } break;
                            }
                        }
                        if (Skip) continue; // Preskocit

                        System.IO.Stream DestStream = null;
                        if (pProgressForm != null) pProgressForm.SetCurrentFileSize(SourceList.CopyMode_GetSize(), DestList.CopyMode_MakeDestinationFullPath(DestFN));

                        while(true) {
                            DestStream = DestList.CopyMode_CreateStreamForWrite(DestFN, out R, Flags.ForceOverwriteDestinationFile, Flags.ForceOverwriteReadOnlyDestinationFile, Flags.ForceOverwriteSystemDestinationFile, Flags.ForceOverwriteSystemDestinationFile); // Vytvorim cielovy subor
                            if (R == 0) break; // OK..

                            switch (R)
                            {
                                case IFileSystem.FS_ERROR_FILE_EXISTS:
                                    {
                                        if (!pProgressForm.ShowEditor_ChooseFromList(string.Format(lang.FILE_EXPLORER_TEXT_39_8_1, DestList.CopyMode_MakeSourceFullPath(RelFN)), lang.FILE_EXPLORER_TEXT_39_8_2, lang.FILE_EXPLORER_TEXT_39_8_3, ref Flags.OverwriteDestinationFile)) return false;
                                    } break;
                                case IFileSystem.FS_ERROR_READ_ONLY_ATTRIBUTE:
                                    {
                                        if (!pProgressForm.ShowEditor_ChooseFromList(string.Format(lang.FILE_EXPLORER_TEXT_39_9_1, DestList.CopyMode_MakeSourceFullPath(RelFN)), lang.FILE_EXPLORER_TEXT_39_9_2, lang.FILE_EXPLORER_TEXT_39_9_3, ref Flags.OverwriteReadOnlyDestinationFile)) return false;
                                    } break;
                                case IFileSystem.FS_ERROR_SYSTEM_ATTRIBUTE:
                                case IFileSystem.FS_ERROR_HIDDEN_ATTRIBUTE:
                                    {
                                        if (!pProgressForm.ShowEditor_ChooseFromList(string.Format(lang.FILE_EXPLORER_TEXT_39_10_1, DestList.CopyMode_MakeSourceFullPath(RelFN)), lang.FILE_EXPLORER_TEXT_39_10_2, lang.FILE_EXPLORER_TEXT_39_10_3, ref Flags.OverwriteSystemDestinationFile)) return false;
                                    } break;
                                default:
                                    {
                                        if (!pProgressForm.ShowEditor_ChooseFromList(string.Format(lang.FILE_EXPLORER_TEXT_39_11_1, DestList.CopyMode_MakeSourceFullPath(RelFN)), lang.FILE_EXPLORER_TEXT_39_11_2, lang.FILE_EXPLORER_TEXT_39_11_3, ref Flags.CreateDestinationFileError)) return false;
                                    } break;
                            }
                        }

                        // Vytvorim GZIP stream
                        System.IO.Compression.GZipStream GStream = new System.IO.Compression.GZipStream(DestStream, System.IO.Compression.CompressionMode.Compress, true);

                        int C;
                        while (true)
                        {

                            if ((pProgressForm != null) && (pProgressForm.WaitForTerminate.BoolValue)) // Prerusenie?
                            {
                                GStream.Close(); GStream.Dispose(); // Uzatvorim GZIP stream
                                SourceList.CopyMode_AbortOperation(); DestList.CopyMode_AbortOperation(); // Prerusim operacie
                                SourceList.CopyMode_CloseReadFile(); // Uzatvorim subor
                                DestList.CopyMode_CloseStreamForWrite(DestStream, true); // Uzatvorim subor
                                return false;
                            }

                            while (true)
                            {
                                C = SourceList.CopyMode_ReadFile(Buffer.Length, Buffer);
                                if (C >= 0) break; // OK?
                                if (!pProgressForm.ShowEditor_ChooseFromList(string.Format(lang.FILE_EXPLORER_TEXT_39_12_1, SourceList.CopyMode_MakeSourceFullPath(RelFN)), lang.FILE_EXPLORER_TEXT_39_12_2, lang.FILE_EXPLORER_TEXT_39_12_3, ref Flags.ReadSourceFileError))
                                {
                                    GStream.Close(); GStream.Dispose(); // Uzatvorim GZIP stream
                                    SourceList.CopyMode_AbortOperation(); DestList.CopyMode_AbortOperation(); // Prerusim operacie
                                    if (pProgressForm != null) pProgressForm.AddTransferredDataSize(-1); // Koniec prenosu
                                    SourceList.CopyMode_CloseReadFile(); // Uzatvorim subor
                                    DestList.CopyMode_CloseStreamForWrite(DestStream, true); // Uzatvorim subor
                                    if (pProgressForm != null) pProgressForm.AddFile(true); // Zaradim subor
                                    return false;
                                }
                            }

                            if (C == 0) break; // Koniec citania?

                            if (Skip) break;

                            while (true)
                            {
                                try
                                {
                                    GStream.Write(Buffer, 0, C); // Zapisem do streamu
                                    break;
                                }
                                catch
                                {
                                }

                                if (!pProgressForm.ShowEditor_ChooseFromList(string.Format(lang.FILE_EXPLORER_TEXT_39_13_1, DestList.CopyMode_MakeDestinationFullPath(RelFN)), lang.FILE_EXPLORER_TEXT_39_13_2, lang.FILE_EXPLORER_TEXT_39_13_3, ref Flags.WriteDestinationFileError))
                                {
                                    GStream.Close(); GStream.Dispose(); // Uzatvorim GZIP stream
                                    SourceList.CopyMode_AbortOperation(); DestList.CopyMode_AbortOperation(); // Prerusim operacie
                                    if (pProgressForm != null) pProgressForm.AddTransferredDataSize(-1); // Koniec prenosu
                                    SourceList.CopyMode_CloseReadFile(); // Uzatvorim subor
                                    DestList.CopyMode_CloseWriteFile(true); // Uzatvorim subor
                                    if (pProgressForm != null) pProgressForm.AddFile(true); // Zaradim subor
                                    return false;
                                }
                            }
                            if (pProgressForm != null) pProgressForm.AddTransferredDataSize(C); // Koniec prenosu
                        }

                        GStream.Close(); GStream.Dispose(); // Uzatvorim GZIP stream
                        DestList.CopyMode_CloseStreamForWrite(DestStream, false); // Uzatvorim subor
                        SourceList.CopyMode_CloseReadFile(); // Uzatvorim zdrojovy subor
                        if (pProgressForm != null) pProgressForm.AddTransferredDataSize(0); // Koniec prenosu
                        if (pProgressForm != null) pProgressForm.AddFile(false); // Zaradim subor
                    }

                }
            }
            return true; // OK
        }

        /// <summary>Prevod OS na retazec</summary>
        /// <param name="pValue">hodnota</param>
        /// <returns>textovy popis</returns>
        public static string OS2String(byte pValue)
        {
            switch (pValue)
            {
                case 0: return "FAT filesystem (MS-DOS, OS/2, NT/Win32)";
                case 1: return "Amiga";
                case 2: return "VMS (or OpenVMS)";
                case 3: return "Unix";
                case 4: return "VM/CMS";
                case 5: return "Atari TOS";
                case 6: return "HPFS filesystem (OS/2, NT)";
                case 7: return "Macintosh";
                case 8: return "Z-System";
                case 9: return "CP/M";
                case 10: return "TOPS-20";
                case 11: return "NTFS filesystem (NT)";
                case 12: return "QDOS";
                case 13: return "Acorn RISCOS";
                default: return "";
            }
        }
    }
}
